#pragma once
#include <cassert>
#include <exception>
#include <stdexcept>
#include "inc_windows_h.hpp"
#include "wxx_error.hpp"

namespace wxx
{
	namespace safe_handles
	{
		class zero_is_invalid
		{
		protected:
			enum { invalid_handle = 0 };

			template<class Ht>
			static bool is_handle_valid(Ht h) throw() 
			{
				return (h != 0);
			}
		};

		class zero_or_minus_one_is_invalid
		{
		protected:
			enum { invalid_handle = 0 };

			template<class Ht>
			static bool is_handle_valid(Ht h) throw() 
			{
				return (h != 0) && 
					   (h != reinterpret_cast<Ht>(-1));
			}
		};

		struct nt_handle : zero_or_minus_one_is_invalid
		{
			typedef HANDLE handle_type;
		
		protected:

			static void dispose(handle_type h) throw(...)
			{
				if (!dispose(h, std::nothrow))
				{
					wxx_error::throw_last_win32();
				}
			}

			static bool dispose(handle_type h, std::nothrow_t) throw() 
			{
				return ::CloseHandle(h) == TRUE;
			}
		};
	}
	
	template<class Policy>
	struct safe_handle : Policy
	{
		typedef typename Policy::handle_type handle_type;

		explicit safe_handle(handle_type handle) throw(...)
			: m_handle(handle)
		{
			if (!Policy::is_handle_valid(handle))
			{
				throw std::invalid_argument("invalid_handle");
			}
		}

		~safe_handle() throw()
		{
			dispose(std::nothrow);
		}

		handle_type handle() const throw()
		{
			return m_handle;
		}

		handle_type attach(handle_type new_handle) throw(...)
		{
			handle_type old_handle = m_handle;

			if (Policy::is_handle_valid(m_handle))
			{
				dispose();
			}

			m_handle = new_handle;
			return old_handle;
		}

		handle_type detach() throw()
		{
			handle_type old_handle = m_handle;

			m_handle = Policy::invalid_handle;
			return old_handle;
		}

	private:

		void dispose() throw(...)
		{
			Policy::dispose(m_handle);
		}
	
		void dispose(std::nothrow_t) throw()
		{
			assert(Policy::dispose(m_handle, std::nothrow));
		}

	private:
		handle_type m_handle;
	};
}